24 Feb 2022

# library(tidyverse)
# library(ncdf4)
# library(raster)
# Let's see if this DEM from NOAA is useful
# juneau <- raster("JuneauBathy.tif")

# plot(juneau)

Filter out the land values by setting >=0

leaning on this: https://www.benjaminbell.co.uk/2019/08/bathymetric-maps-in-r-colour-palettes.html

To figure out a color scheme, need min and max

# Calculate min and max values
# mi <- cellStats(juneau, stat="min")-100
# ma <- cellStats(juneau, stat="max")+100
    # # Break points sequence for below sea level
    # s1 <- seq(from=mi, to=0, by=0 - mi / 50)
    # # Break points sequence for above sea level
    # s2 <- seq(from=0, to=ma, by=ma / 50)
    # 
    #     # Round sequence to nearest 100
    # s1 <- round(s1, -1)
    # s2 <- round(s2, -1)
    # 
    #     # Only show unique numbers
    # s1 <- unique(s1)
    # s2 <- unique(s2)
    # 
    # s1
    # 
    # s2
    # Combine sequences and remove the first value from second sequence
    # s3 <- c(s1, s2[-1])
    # Plot
    # plot(juneau, col=c(blue.col(50), terrain.colors(50)), breaks=s3)
# crop <- extent(-134.8,-134.6,58.45,58.5)
# 
# extent(juneau)
# 
# june.zoom <- setExtent(juneau, crop)
# 
# plot(june.zoom, col=c(blue.col(50), terrain.colors(50)), breaks=s3)

in ggplot

https://stackoverflow.com/questions/33227182/how-to-set-use-ggplot2-to-map-a-raster

library(rnaturalearth)
Support for Spatial objects (`sp`) will be deprecated in {rnaturalearth} and will be removed in a future release of the package. Please use `sf` objects with {rnaturalearth}. For example: `ne_download(returnclass = 'sf')`

Attaching package: ‘rnaturalearth’

The following object is masked from ‘package:rnaturalearthdata’:

    countries110
datafold <- "../data/JuneauBathy.tif"
test <- raster(datafold) 
test_spdf <- as(test, "SpatialPixelsDataFrame")
test_df <- as.data.frame(test_spdf)
colnames(test_df) <- c("value", "x", "y")

zoom.df <- test_df %>%
  filter(x > -134.95, x < -134.76, y > 58.4, y < 58.55)
  #filter(x > -135, x < -134, y > 58.2, y < 58.7) 

zoom.df.w.gray <- zoom.df %>%
  mutate(value = ifelse(value >=0, NA, value)) # set land to NA to shade it gray

# fully zoomed out
full_size_raster <- test_df %>%
  mutate(value = ifelse(value >=0, NA, value)) # set land to NA to shade it gray
# minimum value below sea level
mi <- min(zoom.df$value)

# Break points sequence for below sea level
s1 <- seq(from=mi, to=0, by=0 - mi / 50)

depth.scale <- round(s1, 0)
depth.scale <- unique(depth.scale)

depth.scale
 [1] -262 -257 -252 -247 -241 -236 -231 -226 -220 -215 -210 -205 -199 -194 -189 -184 -178 -173 -168 -163 -157 -152 -147 -142 -136 -131 -126 -121
[29] -115 -110 -105 -100  -94  -89  -84  -79  -73  -68  -63  -58  -52  -47  -42  -37  -31  -26  -21  -16  -10   -5    0
breaks = levels(depth.scale)[floor(seq(1, nlevels(depth.scale), length.out = 10))]

new.depth.scale <- depth.scale[seq(1, length(depth.scale), 3)]
zoomed.out <- ggplot() +  
  geom_tile(data=zoom.df.w.gray, aes(x=x, y=y, fill=value), alpha=0.8) + 
  scale_fill_steps2(low = "navyblue",
                    mid = "dodgerblue4",
  high = "lightsteelblue1",
  midpoint = -155,
  na.value = "grey50",
  breaks = new.depth.scale) +
  coord_equal() +
  theme(panel.border = element_blank(),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.text = element_text(size = 12),
        axis.title.x = element_text(size = 14, margin = margin(t=10)),
        axis.title.y = element_text(size = 14, margin = margin(r=10)),
        axis.line = element_line(color = "black"),
        legend.position="right") +
      theme(legend.text = element_text(size = 8)) + 
      theme(legend.key.height=unit(1.8, "cm")) +
      labs(fill = "Depth (m)",
         x = "Longitude (W)",
         y = "Latitude (N)")

zoomed.out


#ggsave("pdf_outputs/amalgaBathy.pdf")

That’s more zoomed out, but gives a better perspective…

Then here’s more zoomed in on Amalga:

more.zoom.df <- test_df %>%
  filter(x > -134.85, x < -134.77, y > 58.43, y < 58.51) %>%
  mutate(value = ifelse(value >=0, NA, value)) # set land to NA to shade it gray

zoomed.map <- ggplot() +  
  geom_tile(data=more.zoom.df, aes(x=x, y=y, fill=value), alpha=0.8) + 
  scale_fill_steps2(low = "navyblue",
                    mid = "dodgerblue4",
  high = "lightsteelblue1",
  midpoint = -97,
  #space = "Lab",
  na.value = "grey50",
  breaks = new.depth.scale) +
  coord_equal() +
  coord_equal() +
  theme(panel.border = element_blank(),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.text = element_text(size = 12),
        axis.title.x = element_text(size = 14, margin = margin(t=10)),
        axis.title.y = element_text(size = 14, margin = margin(r=10)),
        axis.line = element_line(color = "black"),
        legend.position="right") +
      theme(legend.text = element_text(size = 8)) + 
      theme(legend.key.height=unit(1.8, "cm")) +
      labs(fill = "Depth (m)",
         x = "Longitude (W)",
         y = "Latitude (N)")
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
zoomed.map


#ggsave("amalgaBathyZoom.pdf")

Add the transect line to this map?

# read in the data
# this dataframe is produced in the CTD analysis on the VM
# 08-ctd-cast-data.Rmd
ctd <- read_csv("../data/ctdDataframe.csv")
Rows: 2538 Columns: 16── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (2): ctd_sample, tide
dbl  (13): lat, long, depth_m, salinity, density, pressure_decibar, temp_c, conductivity, sp_conduct, sound_velocity, duration, id, distance
dttm  (1): time
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# just one of the transects (not both AM and PM)
am <- ctd %>%
  filter(tide == "AM_incoming") %>%
  dplyr::select(lat, long, id, distance) %>%
  unique() %>%
  filter(id != 1)

am %>%
  ggplot(aes(x = long, y = lat, label = id)) +
  geom_text()

zoomed.map +
  geom_point(data = am, aes(x = long, y = lat, label = id), size = 1, color = "firebrick2")
Warning: Ignoring unknown aesthetics: label

#ggsave("pdf_outputs/amalgaTransectBathy.pdf", height = 6, width = 7)

Zoom more

highest.res <- more.zoom.df %>%
  filter(x > -134.83, x < -134.779, y > 58.47, y < 58.51) %>%
  mutate(value = ifelse(value >=0, NA, value)) # set land to NA to shade it gray

highest.zoom.plot <- ggplot() +  
  geom_tile(data=highest.res, aes(x=x, y=y, fill = value, color = value)) + 
  scale_fill_steps2(low = "navyblue",
                    mid = "dodgerblue4",
  high = "lightsteelblue1",
  midpoint = -99,
  space = "Lab",
  na.value = "grey50",
  breaks = new.depth.scale) +
  scale_color_steps2(low = "navyblue",
                    mid = "dodgerblue4",
  high = "lightsteelblue1",
  midpoint = -99,
  na.value = "grey50",
  breaks = new.depth.scale) +
  coord_equal() +
  theme(panel.border = element_blank(),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.text = element_text(size = 8),
        axis.title.x = element_text(size = 10, margin = margin(t=10)),
        axis.title.y = element_text(size = 10, margin = margin(r=10)),
        axis.line = element_line(color = "gray50"),
        legend.position="right") +
       theme(legend.text = element_text(size = 6)) + 
      # theme(legend.key.height=unit(1, "cm")) +
      labs(fill = "Depth (m)",
         x = "Longitude (W)",
         y = "Latitude (N)") +
  guides(color = F) +
  scale_y_continuous(expand = c(0,0), breaks = c(58.48, 58.49, 58.50)) +
  scale_x_continuous(expand = c(0,0))
  

highest.zoom.plot

transect2021 <- highest.zoom.plot +
  geom_point(data = am, aes(x = long, y = lat), size = 1, color = "red") +
  #annotate(geom = "label", x = -134.795, y = 58.504, label = "Amalga Harbor", size = 4) +
  #annotate(geom = "label", x = -134.81, y = 58.494, label = "2021 transect", size = 3, fontface = "bold") +
  annotate(geom = "label", x = -134.78999, y = 58.496, label = "pens", size = 3, color = "black") +
    annotate(geom = "label", x = -134.808, y = 58.492, label = "1000m", size = 3, color = "black") +
      annotate(geom = "label", x = -134.825, y = 58.485, label = "2000m", size = 3, color = "black") +
  scale_x_continuous(limits = c(-134.83, -134.78), breaks = c(-134.82, -134.80, -134.78)) +
    scale_y_continuous(limits = c(58.47, 58.51)) 
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
  #labs(title = "2021 transect") +
  #scale_fill_continuous(breaks = c(0, -10, -25, -50, -75, -100, -125, -150, -200))
  
  
#ggsave("pdf_outputs/amalgaTransectBathyZoom.pdf", width = 6, height = 5)

at 560 m, it’s basically the edge of Amalga Harbor.

Add the transect to the zoomed out map:

map2021_out <- zoomed.out +
  geom_point(data = am, aes(x = long, y = lat), size = 1, color = "black") +
  annotate(geom = "text", x = -134.786, y = 58.51, label = "Amalga Harbor", size = 4, color = "white", fontface = "bold") +
  annotate(geom = "label", x = -134.83, y = 58.494, label = "2021 transect", size = 3, fontface = "bold")
  

With these maps, I should be able to combine the two transect years with cowplot or similar.

For the 2022 data, I need the equivalent from the CTD casts for the transect.

CTD data from 2022

ctd.2022 <- read_csv("../data/2022ctdDataframe.csv")
Rows: 1360 Columns: 15── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr   (2): ctd_sample, tide
dbl  (12): lat, long, depth_m, salinity, density, pressure_decibar, temp_c, conductivity, sp_conduct, sound_velocity, duration, depth
dttm  (1): time
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
ctd.2022

# grab the data for May 5 and one tide for data points for the map
one.set.2022 <- ctd.2022 %>%
  filter(str_detect(time, "2022-05-05") &
    tide == "AM_outgoing") %>%
  dplyr::select(ctd_sample, lat, long) %>%
  unique() %>%
  filter(!ctd_sample %in% c("163024", "191935", "175337", "185047")) %>%
  mutate(Year = "2022")

fp <- am %>%
  mutate(Year = "2021") %>%
  bind_rows(one.set.2022)


amalga_plot <- highest.zoom.plot +
  new_scale_color() +
  geom_point(data = fp, aes(x = long, y = lat, shape = Year, color = Year), size = 1.5) +
  annotate(geom = "label", x = -134.789, y = 58.496, label = "pens", size = 3, color = "black") +
    annotate(geom = "label", x = -134.808, y = 58.4865, label = "1000m", size = 3, color = "black") +
      annotate(geom = "label", x = -134.826, y = 58.485, label = "2000m", size = 3, color = "black") +
  scale_color_manual(values = c("coral3", "gold")) +
  scale_shape_manual(values = c(16,17)) +
  theme(
    legend.position = "bottom",
    legend.spacing.x = unit(0, "cm"),
    legend.key = element_rect(fill = "white"),
    legend.spacing = unit(3, "cm")
  ) +
  guides(fill = guide_legend(label.position = "bottom",
                             nrow = 1,
                             keywidth = unit(0.5, "cm"),
                             title.hjust = unit(0.2, "cm"),
                             title.vjust = unit(0.4, "cm")),
         color = guide_legend(override.aes = list(size = 5)))
                             

amalga_plot

ggsave("pdf_outputs/amalgaTransectBathyZoom.png", width = 6, height = 5)


# z.o <- zoomed.out +
#   geom_point(data = one.set.2022, aes(x = long, y = lat), size = 1, color = "white") +
#   annotate(geom = "label", x = -134.8, y = 58.51, label = "Amalga Harbor", size = 4, color = "black") +
#   annotate(geom = "label", x = -134.86, y = 58.48, label = "background", size = 3, color = "black") +
#     annotate(geom = "label", x = -134.80, y = 58.47, label = "transects", size = 3, color = "black") +
#   theme(
#      legend.position = "none"
#   )
# 
#   
# z.i.22 <- highest.zoom.plot +
#   geom_point(data = one.set.2022, aes(x = long, y = lat), size = 1, color = "red") +
#   annotate(geom = "label", x = -134.78999, y = 58.496, label = "pens", size = 3, color = "black") +
#     annotate(geom = "label", x = -134.808, y = 58.492, label = "1000m", size = 3, color = "black") +
#       annotate(geom = "label", x = -134.825, y = 58.485, label = "2000m", size = 3, color = "black") +
#   scale_x_continuous(limits = c(-134.83, -134.78), breaks = c(-134.82, -134.80, -134.78)) +
#     scale_y_continuous(limits = c(58.47, 58.51)) +
#   theme(
#   #   legend.position = "none",
#   #   axis.text.x = element_blank(),
#   #   axis.title.x = element_blank(),
#   #   axis.ticks.x = element_blank(),
#     axis.title.y = element_blank()  
#     )
# 
# z.i.21 <- highest.zoom.plot +
#   geom_point(data = am, aes(x = long, y = lat), size = 1, color = "red") +
#   annotate(geom = "label", x = -134.78999, y = 58.496, label = "pens", size = 3, color = "black") +
#     annotate(geom = "label", x = -134.808, y = 58.492, label = "1000m", size = 3, color = "black") +
#       annotate(geom = "label", x = -134.825, y = 58.485, label = "2000m", size = 3, color = "black") +
#   scale_x_continuous(limits = c(-134.83, -134.78), breaks = c(-134.82, -134.80, -134.78)) +
#     scale_y_continuous(limits = c(58.47, 58.51)) +
#    theme(
#     #legend.position = "none",
#     axis.text.x = element_blank(),
#     axis.title.x = element_blank(),
#     axis.ticks.x = element_blank(),
#     axis.title.y = element_blank()
#   )
library(ggOceanMaps)
ggOceanMaps: Setting data download folder to a temporary folder /var/folders/_5/zm9fzz0d1j139pfm56sqzlfs8qyj5v/T//RtmpQYxlZb. This
means that any downloaded map data need to be downloaded again when you restart R. To avoid this problem, change the default path
to a permanent folder on your computer. Add following lines to your .Rprofile file: {.ggOceanMapsenv <- new.env();
.ggOceanMapsenv$datapath <- 'YourCustomPath'}. You can use usethis::edit_r_profile() to edit the file. '~/ggOceanMapsLargeData'
would make it in a writable folder on most operating systems.

Attaching package: ‘ggOceanMaps’

The following object is masked from ‘package:ggthemes’:

    theme_map

Combine the ak plot and the Amalga plot


(ak_plot | interm_p) / amalga_plot2 + plot_layout(nrow = 2, heights = c(2,3)) +
  plot_annotation(tag_levels = 'A') &
  theme(plot.tag = element_text(size = 8),
        plot.margin = margin(0.1, 0.1, 0.1, 0.1, "cm"))

ggsave("pdf_outputs/amalga_combined_maps.png", width = 7, height = 7)

NOT IN USE BELOW THIS LINE

Bathy transect

library(marmap)
# load datasets
#   data(nw.atlantic); as.bathy(nw.atlantic) -> atl
#   data(nw.atlantic.coast)
# 
# # Example 1. get.transect(), without use of locator()
#   get.transect(atl, -65, 43,-59,40) -> test ; plot(test[,3]~test[,2],type="l")
#   get.transect(atl, -65, 43,-59,40, distance=TRUE) -> test ; plot(test[,4]~test[,3],type="l")
# 
# # Example 2. get.transect(), without use of locator(); pretty plot
#   par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
#   plot(atl, deep=-6000, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
#   lines(nw.atlantic.coast)
# 
#   get.transect(atl, -75, 44,-46,32, loc=FALSE, dis=TRUE) -> test
#   points(test$lon,test$lat,type="l",col="blue",lwd=2,lty=2)
#   plotProfile(test)
# 
# # Example 3. get.transect(), with use of locator(); pretty plot
# ## Not run: 
#   par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
#   plot(atl, deep=-6000, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
#   lines(nw.atlantic.coast)
#   
#   get.transect(atl, loc=TRUE, dis=TRUE, col=2, lty=2) -> test
#   plotProfile(test)
    
## End(Not run)

Very cool, so if I can get my data into that format, I should be able to do something similar.

# -134.7925, -134.8213, 58.4946, 58.4848
# load dataset
juneau <- getNOAA.bathy(lon1 = -140, lon2 = -130,
lat1 = 60, lat2 = 55, resolution = 1)

plot(juneau)
summary(juneau)
# Creating a custom palette of blues
blues <- c("lightsteelblue4", "lightsteelblue3",
"lightsteelblue2", "lightsteelblue1")
# Plotting the bathymetry with different colors for land and sea
plot(juneau, image = TRUE, land = TRUE, lwd = 0.1,
bpal = list(c(0, max(juneau), "grey"),
c(min(juneau),0,blues)))
# Making the coastline more visible
plot(juneau, deep = 0, shallow = 0, step = 0,
lwd = 0.4, add = TRUE)

zoom out even more to give perspective on location:

juneau.out <- getNOAA.bathy(lon1 = -179, lon2 = -125,
lat1 = 75, lat2 = 48, resolution = 8)

plot(juneau.out, image = TRUE, land = TRUE, lwd = 0.1,
bpal = list(c(0, max(juneau.out), "grey"),
c(min(juneau.out),0,blues)))
# Making the coastline more visible
plot(juneau.out, deep = 0, shallow = 0, step = 0,
lwd = 0.4, add = TRUE)

That doesn’t look as good as the DEM, but hopefully it can give us the bathy transect.

# Example 1. get.transect(), without use of locator()
    get.transect(juneau, -134.8213, 58.4848, -134.7925, 58.4946) -> test ; plot(test[,3]~test[,2],type="l")
    get.transect(juneau, -134.8213, 58.4848, -134.7925, 58.4946, distance=TRUE) -> test ; plot(test[,4]~test[,3],type="l")

# Example 2. get.transect(), without use of locator(); pretty plot
    par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
    plot(juneau, deep=-800, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
    #lines(nw.atlantic.coast)

    get.transect(juneau, -134.8213, 58.4848, -134.7925, 58.4946, loc=FALSE, dis=TRUE) -> test
    points(test$lon,test$lat,type="l",col="blue",lwd=2,lty=2)
    plotProfile(test)

# Example 3. get.transect(), with use of locator(); pretty plot
## Not run: 
    par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
    plot(juneau, deep=-800, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
    lines(juneau)

    get.transect(juneau, loc=TRUE, dis=TRUE, col=2, lty=2) -> test
    plotProfile(test)
bathy.df <- highest.res %>%
  rename(longitude = x, latitude = y, depth = value)

bathy.df %>%
  write_csv("amalgaBathyDf.csv")

read.bathy("amalgaBathyDF.csv", header = T)
# Example 1. get.transect(), without use of locator()
    get.transect(highest.res, -134.8213, 58.4848, -134.7925, 58.4946) -> test ; plot(test[,3]~test[,2],type="l")
    get.transect(highest.res, -134.8213, 58.4848, -134.7925, 58.4946, distance=TRUE) -> test ; plot(test[,4]~test[,3],type="l")

# Example 2. get.transect(), without use of locator(); pretty plot
    par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
    plot(test_spdf, deep=-800, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
    #lines(nw.atlantic.coast)

    get.transect(test_spdf, -134.8213, 58.4848, -134.7925, 58.4946, loc=FALSE, dis=TRUE) -> test
    points(test$lon,test$lat,type="l",col="blue",lwd=2,lty=2)
    plotProfile(test)

# Example 3. get.transect(), with use of locator(); pretty plot
## Not run: 
    par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
    plot(test_spdf, deep=-800, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
    lines(test_spdf)

Also this: https://stackoverflow.com/questions/47047623/projectraster-raster-projection-of-bathymetry-data-noaa-nc-in-the-pacific

LS0tCnRpdGxlOiAiYmF0aHltYXAtZm9yLWFtYWxnYSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKMjQgRmViIDIwMjIKCmBgYHtyIGxvYWQtcGFja2FnZXN9CiMgbGlicmFyeSh0aWR5dmVyc2UpCiMgbGlicmFyeShuY2RmNCkKIyBsaWJyYXJ5KHJhc3RlcikKYGBgCgpgYGB7ciBsb2FkLWRhdGF9CiMgTGV0J3Mgc2VlIGlmIHRoaXMgREVNIGZyb20gTk9BQSBpcyB1c2VmdWwKIyBqdW5lYXUgPC0gcmFzdGVyKCJKdW5lYXVCYXRoeS50aWYiKQoKIyBwbG90KGp1bmVhdSkKYGBgCkZpbHRlciBvdXQgdGhlIGxhbmQgdmFsdWVzIGJ5IHNldHRpbmcgPj0wCgpsZWFuaW5nIG9uIHRoaXM6IGh0dHBzOi8vd3d3LmJlbmphbWluYmVsbC5jby51ay8yMDE5LzA4L2JhdGh5bWV0cmljLW1hcHMtaW4tci1jb2xvdXItcGFsZXR0ZXMuaHRtbAoKVG8gZmlndXJlIG91dCBhIGNvbG9yIHNjaGVtZSwgbmVlZCBtaW4gYW5kIG1heApgYGB7cn0KIyBDYWxjdWxhdGUgbWluIGFuZCBtYXggdmFsdWVzCiMgbWkgPC0gY2VsbFN0YXRzKGp1bmVhdSwgc3RhdD0ibWluIiktMTAwCiMgbWEgPC0gY2VsbFN0YXRzKGp1bmVhdSwgc3RhdD0ibWF4IikrMTAwCmBgYAoKYGBge3J9CiAgICAjICMgQnJlYWsgcG9pbnRzIHNlcXVlbmNlIGZvciBiZWxvdyBzZWEgbGV2ZWwKICAgICMgczEgPC0gc2VxKGZyb209bWksIHRvPTAsIGJ5PTAgLSBtaSAvIDUwKQogICAgIyAjIEJyZWFrIHBvaW50cyBzZXF1ZW5jZSBmb3IgYWJvdmUgc2VhIGxldmVsCiAgICAjIHMyIDwtIHNlcShmcm9tPTAsIHRvPW1hLCBieT1tYSAvIDUwKQogICAgIyAKICAgICMgICAgICMgUm91bmQgc2VxdWVuY2UgdG8gbmVhcmVzdCAxMDAKICAgICMgczEgPC0gcm91bmQoczEsIC0xKQogICAgIyBzMiA8LSByb3VuZChzMiwgLTEpCiAgICAjIAogICAgIyAgICAgIyBPbmx5IHNob3cgdW5pcXVlIG51bWJlcnMKICAgICMgczEgPC0gdW5pcXVlKHMxKQogICAgIyBzMiA8LSB1bmlxdWUoczIpCiAgICAjIAogICAgIyBzMQogICAgIyAKICAgICMgczIKYGBgCmBgYHtyfQogICAgIyBDb21iaW5lIHNlcXVlbmNlcyBhbmQgcmVtb3ZlIHRoZSBmaXJzdCB2YWx1ZSBmcm9tIHNlY29uZCBzZXF1ZW5jZQogICAgIyBzMyA8LSBjKHMxLCBzMlstMV0pCmBgYAoKCmBgYHtyfQogICAgIyBQbG90CiAgICAjIHBsb3QoanVuZWF1LCBjb2w9YyhibHVlLmNvbCg1MCksIHRlcnJhaW4uY29sb3JzKDUwKSksIGJyZWFrcz1zMykKYGBgCgpgYGB7cn0KIyBjcm9wIDwtIGV4dGVudCgtMTM0LjgsLTEzNC42LDU4LjQ1LDU4LjUpCiMgCiMgZXh0ZW50KGp1bmVhdSkKIyAKIyBqdW5lLnpvb20gPC0gc2V0RXh0ZW50KGp1bmVhdSwgY3JvcCkKIyAKIyBwbG90KGp1bmUuem9vbSwgY29sPWMoYmx1ZS5jb2woNTApLCB0ZXJyYWluLmNvbG9ycyg1MCkpLCBicmVha3M9czMpCmBgYAoKCgoKCiMjIGluIGdncGxvdAoKaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzMyMjcxODIvaG93LXRvLXNldC11c2UtZ2dwbG90Mi10by1tYXAtYS1yYXN0ZXIKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJhc3RlcikKbGlicmFyeShyYXN0ZXJWaXMpCmxpYnJhcnkocmdkYWwpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkodmlyaWRpcykgICMgYmV0dGVyIGNvbG9ycyBmb3IgZXZlcnlvbmUKbGlicmFyeShnZ3RoZW1lcykgIyB0aGVtZV9tYXAoKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ25ld3NjYWxlKQpsaWJyYXJ5KHJuYXR1cmFsZWFydGhkYXRhKQpsaWJyYXJ5KHJuYXR1cmFsZWFydGgpCmxpYnJhcnkoZ2dzcGF0aWFsKQoKYGBgCgpgYGB7cn0KZGF0YWZvbGQgPC0gIi4uL2RhdGEvSnVuZWF1QmF0aHkudGlmIgp0ZXN0IDwtIHJhc3RlcihkYXRhZm9sZCkgCmBgYAoKCmBgYHtyfQp0ZXN0X3NwZGYgPC0gYXModGVzdCwgIlNwYXRpYWxQaXhlbHNEYXRhRnJhbWUiKQp0ZXN0X2RmIDwtIGFzLmRhdGEuZnJhbWUodGVzdF9zcGRmKQpjb2xuYW1lcyh0ZXN0X2RmKSA8LSBjKCJ2YWx1ZSIsICJ4IiwgInkiKQoKem9vbS5kZiA8LSB0ZXN0X2RmICU+JQogIGZpbHRlcih4ID4gLTEzNC45NSwgeCA8IC0xMzQuNzYsIHkgPiA1OC40LCB5IDwgNTguNTUpCiAgI2ZpbHRlcih4ID4gLTEzNSwgeCA8IC0xMzQsIHkgPiA1OC4yLCB5IDwgNTguNykgCgp6b29tLmRmLncuZ3JheSA8LSB6b29tLmRmICU+JQogIG11dGF0ZSh2YWx1ZSA9IGlmZWxzZSh2YWx1ZSA+PTAsIE5BLCB2YWx1ZSkpICMgc2V0IGxhbmQgdG8gTkEgdG8gc2hhZGUgaXQgZ3JheQoKIyBmdWxseSB6b29tZWQgb3V0CmZ1bGxfc2l6ZV9yYXN0ZXIgPC0gdGVzdF9kZiAlPiUKICBtdXRhdGUodmFsdWUgPSBpZmVsc2UodmFsdWUgPj0wLCBOQSwgdmFsdWUpKSAjIHNldCBsYW5kIHRvIE5BIHRvIHNoYWRlIGl0IGdyYXkKYGBgCgpgYGB7cn0KIyBtaW5pbXVtIHZhbHVlIGJlbG93IHNlYSBsZXZlbAptaSA8LSBtaW4oem9vbS5kZiR2YWx1ZSkKCiMgQnJlYWsgcG9pbnRzIHNlcXVlbmNlIGZvciBiZWxvdyBzZWEgbGV2ZWwKczEgPC0gc2VxKGZyb209bWksIHRvPTAsIGJ5PTAgLSBtaSAvIDUwKQoKZGVwdGguc2NhbGUgPC0gcm91bmQoczEsIDApCmRlcHRoLnNjYWxlIDwtIHVuaXF1ZShkZXB0aC5zY2FsZSkKCmRlcHRoLnNjYWxlCmJyZWFrcyA9IGxldmVscyhkZXB0aC5zY2FsZSlbZmxvb3Ioc2VxKDEsIG5sZXZlbHMoZGVwdGguc2NhbGUpLCBsZW5ndGgub3V0ID0gMTApKV0KCm5ldy5kZXB0aC5zY2FsZSA8LSBkZXB0aC5zY2FsZVtzZXEoMSwgbGVuZ3RoKGRlcHRoLnNjYWxlKSwgMyldCmBgYAoKCmBgYHtyIGJhdGh5LW1hcC1hbWFsZ2F9Cnpvb21lZC5vdXQgPC0gZ2dwbG90KCkgKyAgCiAgZ2VvbV90aWxlKGRhdGE9em9vbS5kZi53LmdyYXksIGFlcyh4PXgsIHk9eSwgZmlsbD12YWx1ZSksIGFscGhhPTAuOCkgKyAKICBzY2FsZV9maWxsX3N0ZXBzMihsb3cgPSAibmF2eWJsdWUiLAogICAgICAgICAgICAgICAgICAgIG1pZCA9ICJkb2RnZXJibHVlNCIsCiAgaGlnaCA9ICJsaWdodHN0ZWVsYmx1ZTEiLAogIG1pZHBvaW50ID0gLTE1NSwKICBuYS52YWx1ZSA9ICJncmV5NTAiLAogIGJyZWFrcyA9IG5ldy5kZXB0aC5zY2FsZSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odD0xMCkpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyPTEwKSksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKSArIAogICAgICB0aGVtZShsZWdlbmQua2V5LmhlaWdodD11bml0KDEuOCwgImNtIikpICsKICAgICAgbGFicyhmaWxsID0gIkRlcHRoIChtKSIsCiAgICAgICAgIHggPSAiTG9uZ2l0dWRlIChXKSIsCiAgICAgICAgIHkgPSAiTGF0aXR1ZGUgKE4pIikKCnpvb21lZC5vdXQKCiNnZ3NhdmUoInBkZl9vdXRwdXRzL2FtYWxnYUJhdGh5LnBkZiIpCmBgYAoKCgpUaGF0J3MgbW9yZSB6b29tZWQgb3V0LCBidXQgZ2l2ZXMgYSBiZXR0ZXIgcGVyc3BlY3RpdmUuLi4KCgpUaGVuIGhlcmUncyBtb3JlIHpvb21lZCBpbiBvbiBBbWFsZ2E6CgoKYGBge3J9Cm1vcmUuem9vbS5kZiA8LSB0ZXN0X2RmICU+JQogIGZpbHRlcih4ID4gLTEzNC44NSwgeCA8IC0xMzQuNzcsIHkgPiA1OC40MywgeSA8IDU4LjUxKSAlPiUKICBtdXRhdGUodmFsdWUgPSBpZmVsc2UodmFsdWUgPj0wLCBOQSwgdmFsdWUpKSAjIHNldCBsYW5kIHRvIE5BIHRvIHNoYWRlIGl0IGdyYXkKCnpvb21lZC5tYXAgPC0gZ2dwbG90KCkgKyAgCiAgZ2VvbV90aWxlKGRhdGE9bW9yZS56b29tLmRmLCBhZXMoeD14LCB5PXksIGZpbGw9dmFsdWUpLCBhbHBoYT0wLjgpICsgCiAgc2NhbGVfZmlsbF9zdGVwczIobG93ID0gIm5hdnlibHVlIiwKICAgICAgICAgICAgICAgICAgICBtaWQgPSAiZG9kZ2VyYmx1ZTQiLAogIGhpZ2ggPSAibGlnaHRzdGVlbGJsdWUxIiwKICBtaWRwb2ludCA9IC05NywKICAjc3BhY2UgPSAiTGFiIiwKICBuYS52YWx1ZSA9ICJncmV5NTAiLAogIGJyZWFrcyA9IG5ldy5kZXB0aC5zY2FsZSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odD0xMCkpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyPTEwKSksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKSArIAogICAgICB0aGVtZShsZWdlbmQua2V5LmhlaWdodD11bml0KDEuOCwgImNtIikpICsKICAgICAgbGFicyhmaWxsID0gIkRlcHRoIChtKSIsCiAgICAgICAgIHggPSAiTG9uZ2l0dWRlIChXKSIsCiAgICAgICAgIHkgPSAiTGF0aXR1ZGUgKE4pIikKCnpvb21lZC5tYXAKCiNnZ3NhdmUoImFtYWxnYUJhdGh5Wm9vbS5wZGYiKQpgYGAKCkFkZCB0aGUgdHJhbnNlY3QgbGluZSB0byB0aGlzIG1hcD8KCgoKCmBgYHtyfQojIHJlYWQgaW4gdGhlIGRhdGEKIyB0aGlzIGRhdGFmcmFtZSBpcyBwcm9kdWNlZCBpbiB0aGUgQ1REIGFuYWx5c2lzIG9uIHRoZSBWTQojIDA4LWN0ZC1jYXN0LWRhdGEuUm1kCmN0ZCA8LSByZWFkX2NzdigiLi4vZGF0YS9jdGREYXRhZnJhbWUuY3N2IikKCmBgYAoKCmBgYHtyfQojIGp1c3Qgb25lIG9mIHRoZSB0cmFuc2VjdHMgKG5vdCBib3RoIEFNIGFuZCBQTSkKYW0gPC0gY3RkICU+JQogIGZpbHRlcih0aWRlID09ICJBTV9pbmNvbWluZyIpICU+JQogIGRwbHlyOjpzZWxlY3QobGF0LCBsb25nLCBpZCwgZGlzdGFuY2UpICU+JQogIHVuaXF1ZSgpICU+JQogIGZpbHRlcihpZCAhPSAxKQoKYW0gJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgbGFiZWwgPSBpZCkpICsKICBnZW9tX3RleHQoKQpgYGAKCmBgYHtyfQp6b29tZWQubWFwICsKICBnZW9tX3BvaW50KGRhdGEgPSBhbSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBsYWJlbCA9IGlkKSwgc2l6ZSA9IDEsIGNvbG9yID0gImZpcmVicmljazIiKQoKI2dnc2F2ZSgicGRmX291dHB1dHMvYW1hbGdhVHJhbnNlY3RCYXRoeS5wZGYiLCBoZWlnaHQgPSA2LCB3aWR0aCA9IDcpCmBgYAoKClpvb20gbW9yZQoKYGBge3Igem9vbS1wbG90LWZvci1tYXBwaW5nLWZpZ3VyZXN9CmhpZ2hlc3QucmVzIDwtIG1vcmUuem9vbS5kZiAlPiUKICBmaWx0ZXIoeCA+IC0xMzQuODMsIHggPCAtMTM0Ljc3OSwgeSA+IDU4LjQ3LCB5IDwgNTguNTEpICU+JQogIG11dGF0ZSh2YWx1ZSA9IGlmZWxzZSh2YWx1ZSA+PTAsIE5BLCB2YWx1ZSkpICMgc2V0IGxhbmQgdG8gTkEgdG8gc2hhZGUgaXQgZ3JheQoKaGlnaGVzdC56b29tLnBsb3QgPC0gZ2dwbG90KCkgKyAgCiAgZ2VvbV90aWxlKGRhdGE9aGlnaGVzdC5yZXMsIGFlcyh4PXgsIHk9eSwgZmlsbCA9IHZhbHVlLCBjb2xvciA9IHZhbHVlKSkgKyAKICBzY2FsZV9maWxsX3N0ZXBzMihsb3cgPSAibmF2eWJsdWUiLAogICAgICAgICAgICAgICAgICAgIG1pZCA9ICJkb2RnZXJibHVlNCIsCiAgaGlnaCA9ICJsaWdodHN0ZWVsYmx1ZTEiLAogIG1pZHBvaW50ID0gLTk5LAogIHNwYWNlID0gIkxhYiIsCiAgbmEudmFsdWUgPSAiZ3JleTUwIiwKICBicmVha3MgPSBuZXcuZGVwdGguc2NhbGUpICsKICBzY2FsZV9jb2xvcl9zdGVwczIobG93ID0gIm5hdnlibHVlIiwKICAgICAgICAgICAgICAgICAgICBtaWQgPSAiZG9kZ2VyYmx1ZTQiLAogIGhpZ2ggPSAibGlnaHRzdGVlbGJsdWUxIiwKICBtaWRwb2ludCA9IC05OSwKICBuYS52YWx1ZSA9ICJncmV5NTAiLAogIGJyZWFrcyA9IG5ldy5kZXB0aC5zY2FsZSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIG1hcmdpbiA9IG1hcmdpbih0PTEwKSksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgbWFyZ2luID0gbWFyZ2luKHI9MTApKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTUwIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsKICAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSkgKyAKICAgICAgIyB0aGVtZShsZWdlbmQua2V5LmhlaWdodD11bml0KDEsICJjbSIpKSArCiAgICAgIGxhYnMoZmlsbCA9ICJEZXB0aCAobSkiLAogICAgICAgICB4ID0gIkxvbmdpdHVkZSAoVykiLAogICAgICAgICB5ID0gIkxhdGl0dWRlIChOKSIpICsKICBndWlkZXMoY29sb3IgPSBGKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSwgYnJlYWtzID0gYyg1OC40OCwgNTguNDksIDU4LjUwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsMCkpCiAgCgpoaWdoZXN0Lnpvb20ucGxvdApgYGAKCgpgYGB7ciBwbG90LWZvci1tYW51c2NyaXB0fQp0cmFuc2VjdDIwMjEgPC0gaGlnaGVzdC56b29tLnBsb3QgKwogIGdlb21fcG9pbnQoZGF0YSA9IGFtLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQpLCBzaXplID0gMSwgY29sb3IgPSAicmVkIikgKwogICNhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuNzk1LCB5ID0gNTguNTA0LCBsYWJlbCA9ICJBbWFsZ2EgSGFyYm9yIiwgc2l6ZSA9IDQpICsKICAjYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgxLCB5ID0gNTguNDk0LCBsYWJlbCA9ICIyMDIxIHRyYW5zZWN0Iiwgc2l6ZSA9IDMsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0Ljc4OTk5LCB5ID0gNTguNDk2LCBsYWJlbCA9ICJwZW5zIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogICAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgwOCwgeSA9IDU4LjQ5MiwgbGFiZWwgPSAiMTAwMG0iLCBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArCiAgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MjUsIHkgPSA1OC40ODUsIGxhYmVsID0gIjIwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0xMzQuODMsIC0xMzQuNzgpLCBicmVha3MgPSBjKC0xMzQuODIsIC0xMzQuODAsIC0xMzQuNzgpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYyg1OC40NywgNTguNTEpKSAKICAjbGFicyh0aXRsZSA9ICIyMDIxIHRyYW5zZWN0IikgKwogICNzY2FsZV9maWxsX2NvbnRpbnVvdXMoYnJlYWtzID0gYygwLCAtMTAsIC0yNSwgLTUwLCAtNzUsIC0xMDAsIC0xMjUsIC0xNTAsIC0yMDApKQogIAogIAojZ2dzYXZlKCJwZGZfb3V0cHV0cy9hbWFsZ2FUcmFuc2VjdEJhdGh5Wm9vbS5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDUpCgpgYGAKYXQgNTYwIG0sIGl0J3MgYmFzaWNhbGx5IHRoZSBlZGdlIG9mIEFtYWxnYSBIYXJib3IuCgoKQWRkIHRoZSB0cmFuc2VjdCB0byB0aGUgem9vbWVkIG91dCBtYXA6CmBgYHtyfQptYXAyMDIxX291dCA8LSB6b29tZWQub3V0ICsKICBnZW9tX3BvaW50KGRhdGEgPSBhbSwgYWVzKHggPSBsb25nLCB5ID0gbGF0KSwgc2l6ZSA9IDEsIGNvbG9yID0gImJsYWNrIikgKwogIGFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSAtMTM0Ljc4NiwgeSA9IDU4LjUxLCBsYWJlbCA9ICJBbWFsZ2EgSGFyYm9yIiwgc2l6ZSA9IDQsIGNvbG9yID0gIndoaXRlIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuODMsIHkgPSA1OC40OTQsIGxhYmVsID0gIjIwMjEgdHJhbnNlY3QiLCBzaXplID0gMywgZm9udGZhY2UgPSAiYm9sZCIpCiAgCgpgYGAKCldpdGggdGhlc2UgbWFwcywgSSBzaG91bGQgYmUgYWJsZSB0byBjb21iaW5lIHRoZSB0d28gdHJhbnNlY3QgeWVhcnMgd2l0aCBjb3dwbG90IG9yIHNpbWlsYXIuCgoKRm9yIHRoZSAyMDIyIGRhdGEsIEkgbmVlZCB0aGUgZXF1aXZhbGVudCBmcm9tIHRoZSBDVEQgY2FzdHMgZm9yIHRoZSB0cmFuc2VjdC4KCkNURCBkYXRhIGZyb20gMjAyMgoKCmBgYHtyIHJlYWQtaW4tZGF0YS1mcm9tLTIwMjJ9CmN0ZC4yMDIyIDwtIHJlYWRfY3N2KCIuLi9kYXRhLzIwMjJjdGREYXRhZnJhbWUuY3N2IikKCmN0ZC4yMDIyCgojIGdyYWIgdGhlIGRhdGEgZm9yIE1heSA1IGFuZCBvbmUgdGlkZSBmb3IgZGF0YSBwb2ludHMgZm9yIHRoZSBtYXAKb25lLnNldC4yMDIyIDwtIGN0ZC4yMDIyICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHRpbWUsICIyMDIyLTA1LTA1IikgJgogICAgdGlkZSA9PSAiQU1fb3V0Z29pbmciKSAlPiUKICBkcGx5cjo6c2VsZWN0KGN0ZF9zYW1wbGUsIGxhdCwgbG9uZykgJT4lCiAgdW5pcXVlKCkgJT4lCiAgZmlsdGVyKCFjdGRfc2FtcGxlICVpbiUgYygiMTYzMDI0IiwgIjE5MTkzNSIsICIxNzUzMzciLCAiMTg1MDQ3IikpICU+JQogIG11dGF0ZShZZWFyID0gIjIwMjIiKQoKZnAgPC0gYW0gJT4lCiAgbXV0YXRlKFllYXIgPSAiMjAyMSIpICU+JQogIGJpbmRfcm93cyhvbmUuc2V0LjIwMjIpCgoKYW1hbGdhX3Bsb3QgPC0gaGlnaGVzdC56b29tLnBsb3QgKwogIG5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmcCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBzaGFwZSA9IFllYXIsIGNvbG9yID0gWWVhciksIHNpemUgPSAxLjUpICsKICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuNzg5LCB5ID0gNTguNDk2LCBsYWJlbCA9ICJwZW5zIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogICAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgwOCwgeSA9IDU4LjQ4NjUsIGxhYmVsID0gIjEwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogICAgICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuODI2LCB5ID0gNTguNDg1LCBsYWJlbCA9ICIyMDAwbSIsIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiY29yYWwzIiwgImdvbGQiKSkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE2LDE3KSkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQuc3BhY2luZy54ID0gdW5pdCgwLCAiY20iKSwKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgbGVnZW5kLnNwYWNpbmcgPSB1bml0KDMsICJjbSIpCiAgKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobGFiZWwucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXl3aWR0aCA9IHVuaXQoMC41LCAiY20iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZS5oanVzdCA9IHVuaXQoMC4yLCAiY20iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZS52anVzdCA9IHVuaXQoMC40LCAiY20iKSksCiAgICAgICAgIGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDUpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKCmFtYWxnYV9wbG90CgpnZ3NhdmUoInBkZl9vdXRwdXRzL2FtYWxnYVRyYW5zZWN0QmF0aHlab29tLnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNSkKCmBgYAoKYGBge3J9CgojIHoubyA8LSB6b29tZWQub3V0ICsKIyAgIGdlb21fcG9pbnQoZGF0YSA9IG9uZS5zZXQuMjAyMiwgYWVzKHggPSBsb25nLCB5ID0gbGF0KSwgc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKwojICAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgsIHkgPSA1OC41MSwgbGFiZWwgPSAiQW1hbGdhIEhhcmJvciIsIHNpemUgPSA0LCBjb2xvciA9ICJibGFjayIpICsKIyAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44NiwgeSA9IDU4LjQ4LCBsYWJlbCA9ICJiYWNrZ3JvdW5kIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuODAsIHkgPSA1OC40NywgbGFiZWwgPSAidHJhbnNlY3RzIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgdGhlbWUoCiMgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKIyAgICkKIyAKIyAgIAojIHouaS4yMiA8LSBoaWdoZXN0Lnpvb20ucGxvdCArCiMgICBnZW9tX3BvaW50KGRhdGEgPSBvbmUuc2V0LjIwMjIsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCksIHNpemUgPSAxLCBjb2xvciA9ICJyZWQiKSArCiMgICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuNzg5OTksIHkgPSA1OC40OTYsIGxhYmVsID0gInBlbnMiLCBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArCiMgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MDgsIHkgPSA1OC40OTIsIGxhYmVsID0gIjEwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MjUsIHkgPSA1OC40ODUsIGxhYmVsID0gIjIwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEzNC44MywgLTEzNC43OCksIGJyZWFrcyA9IGMoLTEzNC44MiwgLTEzNC44MCwgLTEzNC43OCkpICsKIyAgICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoNTguNDcsIDU4LjUxKSkgKwojICAgdGhlbWUoCiMgICAjICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAojICAgIyAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAojICAgIyAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICMgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiMgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSAgCiMgICAgICkKIyAKIyB6LmkuMjEgPC0gaGlnaGVzdC56b29tLnBsb3QgKwojICAgZ2VvbV9wb2ludChkYXRhID0gYW0sIGFlcyh4ID0gbG9uZywgeSA9IGxhdCksIHNpemUgPSAxLCBjb2xvciA9ICJyZWQiKSArCiMgICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuNzg5OTksIHkgPSA1OC40OTYsIGxhYmVsID0gInBlbnMiLCBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArCiMgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MDgsIHkgPSA1OC40OTIsIGxhYmVsID0gIjEwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MjUsIHkgPSA1OC40ODUsIGxhYmVsID0gIjIwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEzNC44MywgLTEzNC43OCksIGJyZWFrcyA9IGMoLTEzNC44MiwgLTEzNC44MCwgLTEzNC43OCkpICsKIyAgICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoNTguNDcsIDU4LjUxKSkgKwojICAgIHRoZW1lKAojICAgICAjbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAojICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAojICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiMgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKQojICAgKQoKYGBgCgoKCmBgYHtyfQpsaWJyYXJ5KGdnT2NlYW5NYXBzKQpgYGAKYGBge3J9CiMganVuZWF1IDwtIGRhdGEuZnJhbWUobG9uID0gLTEzNC41MTYyLAojICAgICAgICAgICAgICAgICAgICAgIGxhdCA9IDU4LjMzNzE4KQojICAgICAgICAgICAgICAgICAgICAgIAojIGlmIG9ubHkgSSBjb3VsZCBmaWd1cmUgb3V0IGhvdyB0byBhbm5vdGF0ZSBhIGdnb2NlYW5tYXAgICAgICAgICAgICAgICAgICAgCmludGVybV9wIDwtIGJhc2VtYXAobGltaXRzID0gYygtMTM4LCAtMTMyLCA1NiwgNTkpLCBiYXRoeW1ldHJ5ID0gVCwgYmF0aHkuc3R5bGUgPSAicmNiIiwgcm90YXRlID0gVCkgKwogIGxhYnMoeCA9ICIgIiwKICAgICAgIHkgPSAiICIpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbihyID0gMC4xLCBsID0gMC4xLCB0ID0gMC41LCBiID0gMC41LCB1bml0ID0gImNtIikKICAgIAogICkKICAKYGBgCgoKYGBge3J9CmFtYWxnYV9wbG90MiA8LSBoaWdoZXN0Lnpvb20ucGxvdCArCiAgI3NjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0xMzQuODI1LCAtMTM0Ljc4NSkpICsKICBuZXdfc2NhbGVfY29sb3IoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZnAsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgc2hhcGUgPSBZZWFyLCBjb2xvciA9IFllYXIpLCBzaXplID0gMS41KSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0Ljc4OSwgeSA9IDU4LjQ5NiwgbGFiZWwgPSAicGVucyIsIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsKICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MDgsIHkgPSA1OC40ODY1LCBsYWJlbCA9ICIxMDAwbSIsIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsKICAgICAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgyNiwgeSA9IDU4LjQ4NSwgbGFiZWwgPSAiMjAwMG0iLCBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImNvcmFsMyIsICJnb2xkIikpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxNiwxNykpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgbGVnZW5kLmJveC5zcGFjaW5nID0gdW5pdCgwLjEsICJjbSIpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDUpKSkgKwogICAgYW5ub3RhdGlvbl9ub3J0aF9hcnJvdyhsb2NhdGlvbiA9ICJibCIsIHdoaWNoX25vcnRoID0gInRydWUiLCAKICAgICAgICBwYWRfeCA9IHVuaXQoMC41LCAiY20iKSwgcGFkX3kgPSB1bml0KDAuOCwgImNtIiksCiAgICAgICAgaGVpZ2h0ID0gdW5pdCgwLjgsICJjbSIpLCB3aWR0aCA9IHVuaXQoMC44LCAiY20iKSwKICAgICAgICBzdHlsZSA9IG5vcnRoX2Fycm93X29yaWVudGVlcmluZyhmaWxsID0gYygiYmxhY2siLCAid2hpdGUiKSkpCgogICMgKSArCiAgIyBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDEsCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXl3aWR0aCA9IHVuaXQoMC41LCAiY20iKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLmhqdXN0ID0gdW5pdCgwLjIsICJjbSIpLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUudmp1c3QgPSB1bml0KDAuNCwgImNtIikpKQogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgCgphbWFsZ2FfcGxvdDIKYGBgCgpgYGB7cn0Kd29ybGQgPC0gbmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHJldHVybmNsYXNzID0gInNmIikKY2xhc3Mod29ybGQpCgpha19wbG90IDwtIGdncGxvdChkYXRhID0gd29ybGQpICsKICBnZW9tX3NmKCkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSAnYWxpY2VibHVlJykpICsKICBjb29yZF9zZih4bGltID0gYygtMTc1LCAtMTI1KSwgeWxpbSA9IGMoNDUsIDY1KSwgZXhwYW5kID0gRikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKDQ1LDU1LDY1KSkgKwogIGFubm90YXRlKCJyZWN0IiwgeG1pbiA9IC0xMzcsIHhtYXggPSAtMTMyLCB5bWluID0gNTYsIHltYXggPSA1OSwgY29sb3IgPSAicmVkIiwgZmlsbCA9IE5BKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gLTE1NSwgeSA9IDUwLCBsYWJlbCA9ICJQYWNpZmljIE9jZWFuIiwgY29sb3IgPSAiZ3JheTIwIiwgc2l6ZSA9IDMuNSkgKwogICBhbm5vdGF0ZSgidGV4dCIsIHggPSAtMTQ1LCB5ID0gNTcsIGxhYmVsID0gIkd1bGYgb2ZcbkFsYXNrYSIsIGNvbG9yID0gImdyYXkyMCIsIHNpemUgPSAzKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbihyID0gMC4xLCBsID0gMC4xLCB0ID0gMC41LCBiID0gMC41LCB1bml0ID0gImNtIikKICApICsKICBhbm5vdGF0aW9uX3NjYWxlKGxvY2F0aW9uID0gImJsIiwgCiAgICAgICAgICAgICAgICAgIGJhcl9jb2xzID0gYygiZ3JheTUwIiwid2hpdGUiKSwKICAgICAgICAgICAgICAgICAgbGluZV93aWR0aCA9IDAuNCwKICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gdW5pdCgwLjIsICJjbSIpLAogICAgICAgICAgICAgICAgICBwYWRfeCA9IHVuaXQoMC4yNSwgImNtIiksCiAgICAgICAgICAgICAgICAgIHBhZF95ID0gdW5pdCgwLjI1LCAiY20iKSwKICAgICAgICAgICAgICAgICAgdGV4dF9wYWQgPSB1bml0KDAuMTUsICJjbSIpKSArCiAgICBhbm5vdGF0aW9uX25vcnRoX2Fycm93KGxvY2F0aW9uID0gImJsIiwgd2hpY2hfbm9ydGggPSAidHJ1ZSIsIAogICAgICAgIHBhZF94ID0gdW5pdCgwLjUsICJjbSIpLCBwYWRfeSA9IHVuaXQoMC44LCAiY20iKSwKICAgICAgICBoZWlnaHQgPSB1bml0KDAuNSwgImNtIiksIHdpZHRoID0gdW5pdCgwLjUsICJjbSIpLAogICAgICAgIHN0eWxlID0gbm9ydGhfYXJyb3dfb3JpZW50ZWVyaW5nKGZpbGwgPSBjKCJncmF5NTAiLCAiZ3JheTIwIikpKQoKYWtfcGxvdApgYGAKCgoKQ29tYmluZSB0aGUgYWsgcGxvdCBhbmQgdGhlIEFtYWxnYSBwbG90CmBgYHtyfQoKKGFrX3Bsb3QgfCBpbnRlcm1fcCkgLyBhbWFsZ2FfcGxvdDIgKyBwbG90X2xheW91dChucm93ID0gMiwgaGVpZ2h0cyA9IGMoMiwzKSkgKwogIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gJ0EnKSAmCiAgdGhlbWUocGxvdC50YWcgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAuMSwgMC4xLCAwLjEsIDAuMSwgImNtIikpCgpnZ3NhdmUoInBkZl9vdXRwdXRzL2FtYWxnYV9jb21iaW5lZF9tYXBzLnBuZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gNykKCmBgYAoKCgoKCgoKCiMjIE5PVCBJTiBVU0UgQkVMT1cgVEhJUyBMSU5FCgoKCiMjIEJhdGh5IHRyYW5zZWN0CgpgYGB7cn0KbGlicmFyeShtYXJtYXApCmBgYAoKCmBgYHtyfQojIGxvYWQgZGF0YXNldHMKIyAJZGF0YShudy5hdGxhbnRpYyk7IGFzLmJhdGh5KG53LmF0bGFudGljKSAtPiBhdGwKIyAJZGF0YShudy5hdGxhbnRpYy5jb2FzdCkKIyAKIyAjIEV4YW1wbGUgMS4gZ2V0LnRyYW5zZWN0KCksIHdpdGhvdXQgdXNlIG9mIGxvY2F0b3IoKQojIAlnZXQudHJhbnNlY3QoYXRsLCAtNjUsIDQzLC01OSw0MCkgLT4gdGVzdCA7IHBsb3QodGVzdFssM11+dGVzdFssMl0sdHlwZT0ibCIpCiMgCWdldC50cmFuc2VjdChhdGwsIC02NSwgNDMsLTU5LDQwLCBkaXN0YW5jZT1UUlVFKSAtPiB0ZXN0IDsgcGxvdCh0ZXN0Wyw0XX50ZXN0WywzXSx0eXBlPSJsIikKIyAKIyAjIEV4YW1wbGUgMi4gZ2V0LnRyYW5zZWN0KCksIHdpdGhvdXQgdXNlIG9mIGxvY2F0b3IoKTsgcHJldHR5IHBsb3QKIyAJcGFyKG1mcm93PWMoMiwxKSxtYWk9YygxLjIsIDEsIDAuMSwgMC4xKSkKIyAJcGxvdChhdGwsIGRlZXA9LTYwMDAsIHNoYWxsb3c9LTEwLCBzdGVwPTEwMDAsIGx3ZD0wLjUsIGNvbD0iZ3JleTUwIixkcmF3bGFiZWxzPVRSVUUpCiMgCWxpbmVzKG53LmF0bGFudGljLmNvYXN0KQojIAojIAlnZXQudHJhbnNlY3QoYXRsLCAtNzUsIDQ0LC00NiwzMiwgbG9jPUZBTFNFLCBkaXM9VFJVRSkgLT4gdGVzdAojIAlwb2ludHModGVzdCRsb24sdGVzdCRsYXQsdHlwZT0ibCIsY29sPSJibHVlIixsd2Q9MixsdHk9MikKIyAJcGxvdFByb2ZpbGUodGVzdCkKIyAKIyAjIEV4YW1wbGUgMy4gZ2V0LnRyYW5zZWN0KCksIHdpdGggdXNlIG9mIGxvY2F0b3IoKTsgcHJldHR5IHBsb3QKIyAjIyBOb3QgcnVuOiAKIyAJcGFyKG1mcm93PWMoMiwxKSxtYWk9YygxLjIsIDEsIDAuMSwgMC4xKSkKIyAJcGxvdChhdGwsIGRlZXA9LTYwMDAsIHNoYWxsb3c9LTEwLCBzdGVwPTEwMDAsIGx3ZD0wLjUsIGNvbD0iZ3JleTUwIixkcmF3bGFiZWxzPVRSVUUpCiMgCWxpbmVzKG53LmF0bGFudGljLmNvYXN0KQojIAkKIyAJZ2V0LnRyYW5zZWN0KGF0bCwgbG9jPVRSVUUsIGRpcz1UUlVFLCBjb2w9MiwgbHR5PTIpIC0+IHRlc3QKIyAJcGxvdFByb2ZpbGUodGVzdCkKCQojIyBFbmQoTm90IHJ1bikKYGBgCgoKVmVyeSBjb29sLCBzbyBpZiBJIGNhbiBnZXQgbXkgZGF0YSBpbnRvIHRoYXQgZm9ybWF0LCBJIHNob3VsZCBiZSBhYmxlIHRvIGRvIHNvbWV0aGluZyBzaW1pbGFyLgoKYGBge3J9CiMgLTEzNC43OTI1LCAtMTM0LjgyMTMsIDU4LjQ5NDYsIDU4LjQ4NDgKIyBsb2FkIGRhdGFzZXQKanVuZWF1IDwtIGdldE5PQUEuYmF0aHkobG9uMSA9IC0xNDAsIGxvbjIgPSAtMTMwLApsYXQxID0gNjAsIGxhdDIgPSA1NSwgcmVzb2x1dGlvbiA9IDEpCgpwbG90KGp1bmVhdSkKc3VtbWFyeShqdW5lYXUpCgpgYGAKCgpgYGB7cn0KIyBDcmVhdGluZyBhIGN1c3RvbSBwYWxldHRlIG9mIGJsdWVzCmJsdWVzIDwtIGMoImxpZ2h0c3RlZWxibHVlNCIsICJsaWdodHN0ZWVsYmx1ZTMiLAoibGlnaHRzdGVlbGJsdWUyIiwgImxpZ2h0c3RlZWxibHVlMSIpCiMgUGxvdHRpbmcgdGhlIGJhdGh5bWV0cnkgd2l0aCBkaWZmZXJlbnQgY29sb3JzIGZvciBsYW5kIGFuZCBzZWEKcGxvdChqdW5lYXUsIGltYWdlID0gVFJVRSwgbGFuZCA9IFRSVUUsIGx3ZCA9IDAuMSwKYnBhbCA9IGxpc3QoYygwLCBtYXgoanVuZWF1KSwgImdyZXkiKSwKYyhtaW4oanVuZWF1KSwwLGJsdWVzKSkpCiMgTWFraW5nIHRoZSBjb2FzdGxpbmUgbW9yZSB2aXNpYmxlCnBsb3QoanVuZWF1LCBkZWVwID0gMCwgc2hhbGxvdyA9IDAsIHN0ZXAgPSAwLApsd2QgPSAwLjQsIGFkZCA9IFRSVUUpCmBgYAoKem9vbSBvdXQgZXZlbiBtb3JlIHRvIGdpdmUgcGVyc3BlY3RpdmUgb24gbG9jYXRpb246CgpgYGB7cn0KanVuZWF1Lm91dCA8LSBnZXROT0FBLmJhdGh5KGxvbjEgPSAtMTc5LCBsb24yID0gLTEyNSwKbGF0MSA9IDc1LCBsYXQyID0gNDgsIHJlc29sdXRpb24gPSA4KQoKcGxvdChqdW5lYXUub3V0LCBpbWFnZSA9IFRSVUUsIGxhbmQgPSBUUlVFLCBsd2QgPSAwLjEsCmJwYWwgPSBsaXN0KGMoMCwgbWF4KGp1bmVhdS5vdXQpLCAiZ3JleSIpLApjKG1pbihqdW5lYXUub3V0KSwwLGJsdWVzKSkpCiMgTWFraW5nIHRoZSBjb2FzdGxpbmUgbW9yZSB2aXNpYmxlCnBsb3QoanVuZWF1Lm91dCwgZGVlcCA9IDAsIHNoYWxsb3cgPSAwLCBzdGVwID0gMCwKbHdkID0gMC40LCBhZGQgPSBUUlVFKQpgYGAKCgoKClRoYXQgZG9lc24ndCBsb29rIGFzIGdvb2QgYXMgdGhlIERFTSwgYnV0IGhvcGVmdWxseSBpdCBjYW4gZ2l2ZSB1cyB0aGUgYmF0aHkgdHJhbnNlY3QuCgpgYGB7cn0KIyBFeGFtcGxlIDEuIGdldC50cmFuc2VjdCgpLCB3aXRob3V0IHVzZSBvZiBsb2NhdG9yKCkKCWdldC50cmFuc2VjdChqdW5lYXUsIC0xMzQuODIxMywgNTguNDg0OCwgLTEzNC43OTI1LCA1OC40OTQ2KSAtPiB0ZXN0IDsgcGxvdCh0ZXN0WywzXX50ZXN0WywyXSx0eXBlPSJsIikKCWdldC50cmFuc2VjdChqdW5lYXUsIC0xMzQuODIxMywgNTguNDg0OCwgLTEzNC43OTI1LCA1OC40OTQ2LCBkaXN0YW5jZT1UUlVFKSAtPiB0ZXN0IDsgcGxvdCh0ZXN0Wyw0XX50ZXN0WywzXSx0eXBlPSJsIikKCiMgRXhhbXBsZSAyLiBnZXQudHJhbnNlY3QoKSwgd2l0aG91dCB1c2Ugb2YgbG9jYXRvcigpOyBwcmV0dHkgcGxvdAoJcGFyKG1mcm93PWMoMiwxKSxtYWk9YygxLjIsIDEsIDAuMSwgMC4xKSkKCXBsb3QoanVuZWF1LCBkZWVwPS04MDAsIHNoYWxsb3c9LTEwLCBzdGVwPTEwMDAsIGx3ZD0wLjUsIGNvbD0iZ3JleTUwIixkcmF3bGFiZWxzPVRSVUUpCgkjbGluZXMobncuYXRsYW50aWMuY29hc3QpCgoJZ2V0LnRyYW5zZWN0KGp1bmVhdSwgLTEzNC44MjEzLCA1OC40ODQ4LCAtMTM0Ljc5MjUsIDU4LjQ5NDYsIGxvYz1GQUxTRSwgZGlzPVRSVUUpIC0+IHRlc3QKCXBvaW50cyh0ZXN0JGxvbix0ZXN0JGxhdCx0eXBlPSJsIixjb2w9ImJsdWUiLGx3ZD0yLGx0eT0yKQoJcGxvdFByb2ZpbGUodGVzdCkKCiMgRXhhbXBsZSAzLiBnZXQudHJhbnNlY3QoKSwgd2l0aCB1c2Ugb2YgbG9jYXRvcigpOyBwcmV0dHkgcGxvdAojIyBOb3QgcnVuOiAKCXBhcihtZnJvdz1jKDIsMSksbWFpPWMoMS4yLCAxLCAwLjEsIDAuMSkpCglwbG90KGp1bmVhdSwgZGVlcD0tODAwLCBzaGFsbG93PS0xMCwgc3RlcD0xMDAwLCBsd2Q9MC41LCBjb2w9ImdyZXk1MCIsZHJhd2xhYmVscz1UUlVFKQoJbGluZXMoanVuZWF1KQoKCWdldC50cmFuc2VjdChqdW5lYXUsIGxvYz1UUlVFLCBkaXM9VFJVRSwgY29sPTIsIGx0eT0yKSAtPiB0ZXN0CglwbG90UHJvZmlsZSh0ZXN0KQpgYGAKYGBge3J9CmJhdGh5LmRmIDwtIGhpZ2hlc3QucmVzICU+JQogIHJlbmFtZShsb25naXR1ZGUgPSB4LCBsYXRpdHVkZSA9IHksIGRlcHRoID0gdmFsdWUpCgpiYXRoeS5kZiAlPiUKICB3cml0ZV9jc3YoImFtYWxnYUJhdGh5RGYuY3N2IikKCnJlYWQuYmF0aHkoImFtYWxnYUJhdGh5REYuY3N2IiwgaGVhZGVyID0gVCkKYGBgCgoKYGBge3J9CiMgRXhhbXBsZSAxLiBnZXQudHJhbnNlY3QoKSwgd2l0aG91dCB1c2Ugb2YgbG9jYXRvcigpCglnZXQudHJhbnNlY3QoaGlnaGVzdC5yZXMsIC0xMzQuODIxMywgNTguNDg0OCwgLTEzNC43OTI1LCA1OC40OTQ2KSAtPiB0ZXN0IDsgcGxvdCh0ZXN0WywzXX50ZXN0WywyXSx0eXBlPSJsIikKCWdldC50cmFuc2VjdChoaWdoZXN0LnJlcywgLTEzNC44MjEzLCA1OC40ODQ4LCAtMTM0Ljc5MjUsIDU4LjQ5NDYsIGRpc3RhbmNlPVRSVUUpIC0+IHRlc3QgOyBwbG90KHRlc3RbLDRdfnRlc3RbLDNdLHR5cGU9ImwiKQoKIyBFeGFtcGxlIDIuIGdldC50cmFuc2VjdCgpLCB3aXRob3V0IHVzZSBvZiBsb2NhdG9yKCk7IHByZXR0eSBwbG90CglwYXIobWZyb3c9YygyLDEpLG1haT1jKDEuMiwgMSwgMC4xLCAwLjEpKQoJcGxvdCh0ZXN0X3NwZGYsIGRlZXA9LTgwMCwgc2hhbGxvdz0tMTAsIHN0ZXA9MTAwMCwgbHdkPTAuNSwgY29sPSJncmV5NTAiLGRyYXdsYWJlbHM9VFJVRSkKCSNsaW5lcyhudy5hdGxhbnRpYy5jb2FzdCkKCglnZXQudHJhbnNlY3QodGVzdF9zcGRmLCAtMTM0LjgyMTMsIDU4LjQ4NDgsIC0xMzQuNzkyNSwgNTguNDk0NiwgbG9jPUZBTFNFLCBkaXM9VFJVRSkgLT4gdGVzdAoJcG9pbnRzKHRlc3QkbG9uLHRlc3QkbGF0LHR5cGU9ImwiLGNvbD0iYmx1ZSIsbHdkPTIsbHR5PTIpCglwbG90UHJvZmlsZSh0ZXN0KQoKIyBFeGFtcGxlIDMuIGdldC50cmFuc2VjdCgpLCB3aXRoIHVzZSBvZiBsb2NhdG9yKCk7IHByZXR0eSBwbG90CiMjIE5vdCBydW46IAoJcGFyKG1mcm93PWMoMiwxKSxtYWk9YygxLjIsIDEsIDAuMSwgMC4xKSkKCXBsb3QodGVzdF9zcGRmLCBkZWVwPS04MDAsIHNoYWxsb3c9LTEwLCBzdGVwPTEwMDAsIGx3ZD0wLjUsIGNvbD0iZ3JleTUwIixkcmF3bGFiZWxzPVRSVUUpCglsaW5lcyh0ZXN0X3NwZGYpCgpgYGAKCgoKCgoKQWxzbyB0aGlzOgpodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80NzA0NzYyMy9wcm9qZWN0cmFzdGVyLXJhc3Rlci1wcm9qZWN0aW9uLW9mLWJhdGh5bWV0cnktZGF0YS1ub2FhLW5jLWluLXRoZS1wYWNpZmljCgo=